import {
  addIcon, App,
  Editor,
  EditorPosition,
  EditorSuggest,
  EditorSuggestContext,
  EditorSuggestTriggerInfo,
  MarkdownView, Notice,
  Plugin, PluginSettingTab, Setting, TFile, TFolder, Vault
} from "obsidian";

class RegexDef {
  regexText: string;
  enabled: boolean;
  constructor(regexText: string = "", enabled: boolean = true) {
    this.regexText = regexText;
    this.enabled = enabled;
  }
}
interface PluginSettings {
  replacePageRegexes: Array<RegexDef>,
  replacePageTemplate: string,
  replacePageDefaultReplacements: string,
}

const DEFAULT_SETTINGS: PluginSettings = {
  replacePageRegexes: [new RegexDef("`(.*)__([0-9a-f]{5,40})(\\.\\S+)?", true), new RegexDef("(\\S*)__([0-9a-f]{5,40})(\\.\\S+)?", true)],
  // replacePageRegex: '"(.*)__([0-9a-f]{5,40})(\.\S+)?"', // \"(.*)__([0-9a-f]{5,40})(\\.\\S+)?\" (将此完整copy至插件)
  // replacePageRegex2: "(\S*)__([0-9a-f]{5,40})(\.\S+)?", // (\\S*)__([0-9a-f]{5,40})(\\.\\S+)?
  replacePageTemplate: "http://localhost:3000/{{%2}}{{%3}}#~:~hint={{%1}}",
  replacePageDefaultReplacements: "{{%3}}=.html",
}

//Add chevron-up-square icon from lucide for mobile toolbar (temporary until Obsidian updates to Lucide v0.130.0)
addIcon("chevron-up-square", `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-up-square"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"></rect><polyline points="8,14 12,10 16,14"></polyline></svg>`);
export default class OpenInBrowserPlugin extends Plugin {
  settings: PluginSettings

  async onload() {
    await this.loadSettings();
    this.addCommand({
      id: "open-selection-in-browser",
      name: "Open Selection in Browser",
      icon: "plus-square",
      checkCallback: (checking: boolean) => {
        if (checking)
          return !!this.app.workspace.getActiveViewOfType(MarkdownView);
        this.openSelectionInBrowser();
      },
    });

    // add settings tab
    this.addSettingTab(new SettingTab(this.app, this));
  }

  async loadSettings() {
    this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
  }

  async saveSettings() {
    await this.saveData(this.settings);
  }

  openSelectionInBrowser(){
    const mdView = this.app.workspace.getActiveViewOfType(MarkdownView);

    if (!mdView) return false;
    if (mdView.editor == undefined) return false;

    const doc = mdView.editor;
    let text = doc.getSelection();
    if (text.trim() === "") {
      const cursorPosition = doc.getCursor();
      text = doc.getLine(cursorPosition.line);
    }

    const url = this.extractUrlInChain(text);
    if (!url.startsWith("https://") && !url.startsWith("http://")) {
      console.log(url);
      new Notice("url is not valid");
      return false;
    }

    // const url = "https://blog.csdn.net/qq_19309473";
    const exec = require('child_process').exec;
    console.log(process.platform);
    switch (process.platform) {
      case "darwin":
        exec("open" + url);
        break;
      case "win32":
        exec("start " + url);
        break;
      default:
        exec("xdg-open", [url]);
    }
  }

  extractUrlInChain(selectionText: string): string {
    const regexList: Array<RegExp> = [];
    this.settings.replacePageRegexes.forEach((regexDef, index) => {
      const regex: RegExp = regexDef.regexText.trim() === "" ? null : new RegExp(regexDef.regexText, 'g') ;
      if (regexDef.enabled && regex) {
        regexList.push(regex);
      }
    });

    // const regex: RegExp = this.settings.replacePageRegex.trim() === "" ? null : new RegExp(this.settings.replacePageRegex, 'g') ;
    // const regex2: RegExp = this.settings.replacePageRegex2.trim() === "" ? null : new RegExp(this.settings.replacePageRegex2, 'g');
    const template = this.settings.replacePageTemplate.trim();
    const defaultParamList = this.settings.replacePageDefaultReplacements.split(",").map(item => item.trim());
    const defaultParamMap = new Map;
    for (const defaultParam of defaultParamList){
      const [key, value] = defaultParam.split("=").map(item => item.trim());
      defaultParamMap.set(key, value);
    }

    let extractedUrl = selectionText;
    for (const regex of regexList) {
      extractedUrl = this.extractUrl(extractedUrl, regex, template, defaultParamMap);
    }
    // if (regex) {
    //   extractedUrl = this.extractUrl(selectionText, regex, template, defaultParamList);
    // } else if (regex2) {
    //   extractedUrl = this.extractUrl(extractedUrl, regex2, template, defaultParamList);
    // }
    return extractedUrl;
  }
  /*
  替换给定text范围内符合正则表达式的内容，替换为tempalte内容
   */
  extractUrl(text: string, regex: RegExp, template: string, defaultParamMap: Map<string, string>): string {
    let match;
    let replacedUrlTemplate = text;
    while ((match = regex.exec(text)) != null) {
      const full = match[0];
      const listToReplace = match.map(item => {
        if (item) {
          return item.toString();
        } else {
          return "";
        }
      });
      // replacedText = replacedText.replace(full, this.replaceTemplate(template, listToReplace, defaultParamList));
      replacedUrlTemplate = this.replaceTemplate(template, listToReplace, defaultParamMap);
    }
    return replacedUrlTemplate;
  }

  replaceTemplate(template: string, paramList: Array<string>, defaultParamMap: Map<string, string>): string{
    // const defaultParamList = ["na", "na", "na", ".html"];
    let text = template;
    let paramEscapeList = paramList.map((item, index) => { // JM：替换成的目标字符串
      if (item && item != "") {
        return item.replaceAll("(", "%28").replaceAll(")", "%29")
            .replaceAll("[", "%5B").replaceAll("]", "%5D").replaceAll(" ", "%20");
      } else {
        const key = `{{%${index}}}`;
        if (defaultParamMap.has(key)){
          return defaultParamMap.get(key);
        } else {
          return "na";
        }
        // return defaultParamList[index];
      }
    });
    for (let i=1;i<=paramEscapeList.length;i++) {
      text = text.replace("{{%"+i.toString()+"}}", paramEscapeList[i]); // JM: 这里跳过paramList[0]因为[0]是full match
    }
    return text;
  }
}

class SettingTab extends PluginSettingTab {
  plugin: OpenInBrowserPlugin;

  constructor(app: App, plugin: OpenInBrowserPlugin) {
    super(app, plugin);
    this.plugin = plugin;
  }

  arraymove<T>(
      arr: T[],
      fromIndex: number,
      toIndex: number
  ): void {
    if (toIndex < 0 || toIndex === arr.length) {
      return;
    }
    const element = arr[fromIndex];
    arr[fromIndex] = arr[toIndex];
    arr[toIndex] = element;
  }

  display(): void {
    const { containerEl } = this;
    containerEl.empty();
    this.containerEl.createEl("h2", { text: "Define Regex" });

    const desc = document.createDocumentFragment();
    desc.append(
        "Define regex."
    );

    new Setting(this.containerEl).setDesc(desc);
    this.plugin.settings.replacePageRegexes.forEach((regexText, index) => {
      const s = new Setting(this.containerEl)
          .addText(text => text
              .setPlaceholder('`(.*)__([0-9a-f]{5,40})(\.\S+)?`')
              .setValue(this.plugin.settings.replacePageRegexes[index].regexText)
              .onChange(async (value) => {
                    this.plugin.settings.replacePageRegexes[index].regexText = value;
                    await this.plugin.saveSettings();
                  }
              ))
          .addToggle(toogle => toogle
              .setValue(this.plugin.settings.replacePageRegexes[index].enabled)
              .onChange(async (value) => {
                    this.plugin.settings.replacePageRegexes[index].enabled = value;
                    await this.plugin.saveSettings();
                  }
              ))
          .addExtraButton((cb) => {
            cb.setIcon("up-chevron-glyph")
                .setTooltip("Move up")
                .onClick(() => {
                  this.arraymove(
                      this.plugin.settings.replacePageRegexes,
                      index,
                      index - 1
                  );
                  this.plugin.saveSettings();
                  this.display();
                });
          })
          .addExtraButton((cb) => {
            cb.setIcon("down-chevron-glyph")
                .setTooltip("Move down")
                .onClick(() => {
                  this.arraymove(
                      this.plugin.settings.replacePageRegexes,
                      index,
                      index + 1
                  );
                  this.plugin.saveSettings();
                  this.display();
                });
          })
          .addExtraButton((cb) => {
            cb.setIcon("cross")
                .setTooltip("Delete")
                .onClick(() => {
                  this.plugin.settings.replacePageRegexes.splice(
                      index,
                      1
                  );
                  this.plugin.saveSettings();
                  // Force refresh
                  this.display();
                });
          })
    });
    new Setting(this.containerEl).addButton((cb) => {
      cb.setButtonText("Add")
          .setCta()
          .onClick(() => {
            this.plugin.settings.replacePageRegexes.push(new RegexDef());
            this.plugin.saveSettings();
            // Force refresh
            this.display();
          });
    });
    // new Setting(containerEl)
    //     .setName('Replace Page Regex')
    //     .setDesc('regex for replace page text.')
    //     .setClass('long-description-setting-item')
    //     .addText(text => text
    //         .setPlaceholder('input valid regular expression.')
    //         .setValue(this.plugin.settings.replacePageRegex)
    //         .onChange(async (value) => {
    //               this.plugin.settings.replacePageRegex = value;
    //               await this.plugin.saveSettings();
    //             }
    //         ));
    // new Setting(containerEl)
    //     .setName('Replace Page Regex2')
    //     .setDesc('regex2 for replace page text.')
    //     .setClass('long-description-setting-item')
    //     .addText(text => text
    //         .setPlaceholder('input valid regular expression.')
    //         .setValue(this.plugin.settings.replacePageRegex2)
    //         .onChange(async (value) => {
    //               this.plugin.settings.replacePageRegex2 = value;
    //               await this.plugin.saveSettings();
    //             }
    //         ));
    new Setting(containerEl)
        .setName('Replace Page Template')
        .setDesc('{{%1}} is replaced by regex match group 1.')
        .setClass('long-description-setting-item')
        .addText(text => text
            .setPlaceholder('e.g. [本地知识库](http://localhost:3000/{{%2}}{{%3}}#~:~hint={{%1}})')
            .setValue(this.plugin.settings.replacePageTemplate)
            .onChange(async (value) => {
                  this.plugin.settings.replacePageTemplate = value;
                  await this.plugin.saveSettings();
                }
            ));
    new Setting(containerEl)
        .setName('Replace Page Default Replacements')
        .setDesc('use comma(,) as separator, define the default value if regex match group empty.')
        .addText(text => text
            .setPlaceholder("e.g. '{{%3}}=.html'")
            .setValue(this.plugin.settings.replacePageDefaultReplacements)
            .onChange(async (value) => {
                  this.plugin.settings.replacePageDefaultReplacements = value;
                  await this.plugin.saveSettings();
                }
            ));
  }
}